package com.hibernate.hrm.service.contract;

import com.hibernate.hrm.dao.ContractFulfillmentRepository;
import com.hibernate.hrm.dao.ContractRepository;
import com.hibernate.hrm.dao.DepartmentRepository;
import com.hibernate.hrm.dao.OperateLogRepository;
import com.hibernate.hrm.entity.*;
import com.hibernate.hrm.entity.dto.contract.*;
import jakarta.persistence.EntityNotFoundException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.time.LocalDateTime;
import java.util.List;

@Service
@RequiredArgsConstructor
public class ContractServiceImpl implements ContractService {

    private final ContractRepository contractRepository;
    private final DepartmentRepository departmentRepository;
    private final OperateLogRepository operateLogRepository;
    private final HttpServletRequest request;
    private final ContractFulfillmentRepository fulfillmentRepository;
    private final LegalClauseAnalyzer legalClauseAnalyzer;
    private final ElectronicSignatureVerifier signatureVerifier;

    @Override
    public Page<ContractDTO> getContractList(ContractQueryDTO queryDTO, User currentUser) {
        Pageable pageable = queryDTO.getPageable();

        // 获取数据
        Page<Contract> contracts;
        if ("ROLE_STAFF".equals(currentUser.getRoleId().getRoleCode())) {
            contracts = contractRepository.findByUser(
                    currentUser.getUserId(),
                    queryDTO.getContractType(),
                    queryDTO.getStatus(),
                    pageable);
        } else if ("ROLE_DEPT_MGR".equals(currentUser.getRoleId().getRoleCode())) {
            List<Department> accessibleDepartments = getAccessibleDepartments(currentUser);
            contracts = contractRepository.findByDepartments(
                    accessibleDepartments,
                    queryDTO.getContractType(),
                    queryDTO.getStatus(),
                    pageable);
        } else {
            contracts = contractRepository.findByFilters(
                    queryDTO.getDepartmentId(),
                    queryDTO.getContractType(),
                    queryDTO.getStatus(),
                    pageable);
        }

        // 确保返回有效的分页对象，即使没有数据
        if (contracts.getContent().isEmpty() && contracts.getNumber() > 0) {
            pageable = PageRequest.of(0, pageable.getPageSize(), pageable.getSort());
            return getContractList(queryDTO, currentUser); // 递归调用获取第一页
        }

        return contracts.map(ContractDTO::fromEntity);
    }

    @Override
    public List<Department> getAccessibleDepartments(User currentUser) {
        // ROLE_DEPT_MGR 只能管理自己部门及子部门
        if (currentUser.getRoleId().getRoleCode().equals("ROLE_DEPT_MGR")) {
            return departmentRepository.findByDepartmentParentID(currentUser.getDepartmentID());
        }
        // 其他角色可以查看所有部门
        return departmentRepository.findAll();
    }

    @Override
    @Transactional
    public void terminateContract(Integer contractId, User operator) {
        Contract contract = contractRepository.findById(contractId)
                .orElseThrow(() -> new EntityNotFoundException("合同不存在"));

        // 检查合同状态是否为"履行中"或"逾期"
        if (contract.getStatus() != Contract.ContractStatus.履行中 &&
                contract.getStatus() != Contract.ContractStatus.逾期) {
            throw new IllegalStateException("只有履行中或逾期的合同可以终止");
        }

        // 更新合同状态
        contract.setStatus(Contract.ContractStatus.已终止);
        contract.setUpdateTime(LocalDateTime.now());
        contractRepository.save(contract);

        // 记录操作日志
        recordContractTerminationLog(operator, contract);
    }

    private void recordContractTerminationLog(User operator, Contract contract) {
        OperateLog log = new OperateLog();
        log.setUserId(operator.getUserId());
        log.setOperateType("update");
        log.setOperateDetail(String.format(
                "用户[%s](ID:%d)终止了合同[%s](ID:%d)",
                operator.getRealName(),
                operator.getUserId(),
                contract.getContractName(),
                contract.getContractId()
        ));
        log.setOperateTime(LocalDateTime.now());
        log.setIpAddress(getClientIpAddress());
        log.setStatus(1);
        operateLogRepository.save(log);
    }

    //合同详情
    @Override
    public ContractDetailDTO getContractDetail(Integer contractId, User currentUser) {
        Contract contract = contractRepository.findById(contractId)
                .orElseThrow(() -> new EntityNotFoundException("合同不存在"));

        ContractDetailDTO dto = ContractDetailDTO.fromEntity(contract, currentUser);

        // 添加履行记录
        dto.setFulfillments(fulfillmentRepository.findByContractOrderByFulfillmentDateDesc(contract));

        // 添加法律条款解析
        dto.setLegalClauseAnalysis(analyzeLegalClauses(contractId));

        // 验证电子签名
        dto.setIsSignatureValid(verifyElectronicSignature(contractId));

        return dto;
    }

    @Override
    public boolean verifyElectronicSignature(Integer contractId) {
        // 实际项目中应调用区块链存证平台API验证
        return signatureVerifier.verify(contractId);
    }

    @Override
    public String analyzeLegalClauses(Integer contractId) {
        // 实际项目中应根据合同类型解析相关法律条款
        return legalClauseAnalyzer.analyze(contractId);
    }

    //导出合同
    @Override
    @Transactional
    public byte[] exportContractToWord(Integer contractId, User currentUser) {
        Contract contract = contractRepository.findById(contractId)
                .orElseThrow(() -> new EntityNotFoundException("合同不存在"));

        ContractDetailDTO detail = getContractDetail(contractId, currentUser);
        ContractExportDTO exportDTO = ContractExportDTO.fromContractDetail(detail);

        try {
            XWPFDocument document = new XWPFDocument();

            // 创建标题
            XWPFParagraph titlePara = document.createParagraph();
            titlePara.setAlignment(ParagraphAlignment.CENTER);
            XWPFRun titleRun = titlePara.createRun();
            titleRun.setText(exportDTO.getContractName());
            titleRun.setBold(true);
            titleRun.setFontSize(18);

            // 添加合同基本信息
            addContractInfo(document, exportDTO);

            // 根据合同类型添加不同内容
            switch (detail.getContractType()) {
                case 劳动合同:
                    addLaborContractContent(document, exportDTO);
                    break;
                case 委托合同:
                    addEntrustContractContent(document, exportDTO);
                    break;
                case 服务协议:
                    addServiceContractContent(document, exportDTO);
                    break;
            }

            // 添加通用条款
            addGeneralTerms(document);

            // 转换为字节数组
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            document.write(out);
            document.close();

            // 记录导出操作日志
            recordExportLog(currentUser, contract);

            return out.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException("导出合同失败", e);
        }
    }

    private void recordExportLog(User operator, Contract contract) {
        OperateLog log = new OperateLog();
        log.setUserId(operator.getUserId());
        log.setOperateType("export");
        log.setOperateDetail(String.format(
                "用户[%s](ID:%d)导出了合同[%s](ID:%d)",
                operator.getRealName(),
                operator.getUserId(),
                contract.getContractName(),
                contract.getContractId()
        ));
        log.setOperateTime(LocalDateTime.now());
        log.setIpAddress(getClientIpAddress());
        log.setStatus(1);
        operateLogRepository.save(log);
    }

    private void addContractInfo(XWPFDocument document, ContractExportDTO dto) {
        XWPFParagraph para = document.createParagraph();
        XWPFRun run = para.createRun();
        run.setText("合同编号: " + dto.getContractNumber());
        run.addBreak();
        run.setText("合同类型: " + dto.getContractType());
        run.addBreak();
        run.setText("状态: " + dto.getStatus());
        run.addBreak();
        run.setText("甲方(委托方): " + dto.getPartyAName() + " (" + dto.getPartyADepartment() + ")");
        run.addBreak();
        run.setText("乙方(受托方): " + dto.getPartyBName() + " (" + dto.getPartyBDepartment() + ")");
        run.addBreak();
        run.setText("合同期限: " + dto.getEffectiveDate() + " 至 " + dto.getExpiryDate());
        run.addBreak();

        if (dto.getPenaltyRate() != null && !dto.getPenaltyRate().equals("无")) {
            run.setText("违约金条款: " + dto.getPenaltyRate());
            run.addBreak();
        }
    }

    private void addLaborContractContent(XWPFDocument document, ContractExportDTO dto) {
        XWPFParagraph para = document.createParagraph();
        para.setSpacingAfter(200);

        XWPFRun run = para.createRun();
        run.setText("根据《中华人民共和国劳动法》和《中华人民共和国劳动合同法》，甲乙双方签订本劳动合同，共同遵守以下条款：");
        run.addBreak();

        addSection(document, "一、工作内容",
                "乙方同意在甲方从事" + dto.getWorkPosition() + "工作，工作地点为" + dto.getWorkLocation() + "。");

        addSection(document, "二、合同期限",
                "合同期限自" + dto.getEffectiveDate() + "至" + dto.getExpiryDate() + "。");
    }

    private void addEntrustContractContent(XWPFDocument document, ContractExportDTO dto) {
        XWPFParagraph para = document.createParagraph();
        para.setSpacingAfter(200);

        XWPFRun run = para.createRun();
        run.setText("根据《中华人民共和国民法典》，甲方委托乙方办理相关事务，达成如下协议：");
        run.addBreak();

        addSection(document, "一、委托事项", "甲方委托乙方处理" + dto.getEntrustContent() + "。");
        addSection(document, "二、委托权限", "乙方在" + dto.getEntrustScope() + "范围内代表甲方处理上述事项。");
    }

    private void addServiceContractContent(XWPFDocument document, ContractExportDTO dto) {
        XWPFParagraph para = document.createParagraph();
        para.setSpacingAfter(200);

        XWPFRun run = para.createRun();
        run.setText("根据《中华人民共和国民法典》，甲方接受乙方提供的服务，达成如下协议：");
        run.addBreak();

        addSection(document, "一、服务内容", dto.getServiceContent());
        addSection(document, "二、服务期限", "自" + dto.getEffectiveDate() + "至" + dto.getExpiryDate() + "。");
    }

    private void addGeneralTerms(XWPFDocument document) {
        addSection(document, "争议解决",
                "本协议履行过程中发生的争议，双方应协商解决；协商不成的，任何一方均可向甲方所在地人民法院提起诉讼。");

        addSection(document, "其他",
                "1. 本合同一式两份，甲乙双方各执一份，具有同等法律效力。\n" +
                        "2. 本合同自双方签字盖章之日起生效。");
    }

    private void addSection(XWPFDocument document, String title, String content) {
        XWPFParagraph titlePara = document.createParagraph();
        XWPFRun titleRun = titlePara.createRun();
        titleRun.setText(title);
        titleRun.setBold(true);

        XWPFParagraph contentPara = document.createParagraph();
        XWPFRun contentRun = contentPara.createRun();
        contentRun.setText(content);
        contentRun.addBreak();
    }

    //编辑合同
    @Override
    public ContractDetailDTO getContractForEdit(Integer contractId, User currentUser) {
        Contract contract = contractRepository.findById(contractId)
                .orElseThrow(() -> new EntityNotFoundException("合同不存在"));

        // 检查权限
        if (!hasEditPermission(contract, currentUser)) {
            throw new AccessDeniedException("无权编辑此合同");
        }

        return ContractDetailDTO.fromEntity(contract, currentUser);
    }

    @Override
    @Transactional
    public void updateContract(ContractEditDTO editDTO, User currentUser) {
        Contract contract = contractRepository.findById(editDTO.getContractId())
                .orElseThrow(() -> new EntityNotFoundException("合同不存在"));

        // 检查权限
        if (!hasEditPermission(contract, currentUser)) {
            throw new AccessDeniedException("无权编辑此合同");
        }

        // 更新合同信息
        contractRepository.updateContract(
                editDTO.getContractId(),
                editDTO.getContractName(),
                editDTO.getContractType(),
                editDTO.getSigningPartyAId(),
                editDTO.getSigningPartyBId(),
                editDTO.getEffectiveDate(),
                editDTO.getExpiryDate(),
                editDTO.getDepartmentId(),
                editDTO.getIsTransferable(),
                editDTO.getPenaltyRate(),
                editDTO.getWorkPosition(),
                editDTO.getWorkLocation(),
                editDTO.getEntrustContent(),
                editDTO.getEntrustScope(),
                editDTO.getServiceContent()
        );

        // 记录操作日志
        recordContractUpdateLog(currentUser, contract);
    }

    private boolean hasEditPermission(Contract contract, User currentUser) {
        int roleIdValue = currentUser.getRoleId().getRoleId();
        int userDeptId = currentUser.getDepartmentID().getDepartmentID();
        int contractDeptId = contract.getDepartment().getDepartmentID();

        return roleIdValue < 4 || (roleIdValue == 4 && contractDeptId == userDeptId);
    }

    private void recordContractUpdateLog(User operator, Contract contract) {
        OperateLog log = new OperateLog();
        log.setUserId(operator.getUserId());
        log.setOperateType("update");
        log.setOperateDetail(String.format(
                "用户[%s](ID:%d)更新了合同[%s](ID:%d)",
                operator.getRealName(),
                operator.getUserId(),
                contract.getContractName(),
                contract.getContractId()
        ));
        log.setOperateTime(LocalDateTime.now());
        log.setIpAddress(getClientIpAddress());
        log.setStatus(1);
        operateLogRepository.save(log);
    }

    private String getClientIpAddress() {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}